/*
*  Copyright (c) 2001 Sun Microsystems, Inc.  All rights
*  reserved.
*
*  Redistribution and use in source and binary forms, with or without
*  modification, are permitted provided that the following conditions
*  are met:
*
*  1. Redistributions of source code must retain the above copyright
*  notice, this list of conditions and the following disclaimer.
*
*  2. Redistributions in binary form must reproduce the above copyright
*  notice, this list of conditions and the following discalimer in
*  the documentation and/or other materials provided with the
*  distribution.
*
*  3. The end-user documentation included with the redistribution,
*  if any, must include the following acknowledgment:
*  "This product includes software developed by the
*  Sun Microsystems, Inc. for Project JXTA."
*  Alternately, this acknowledgment may appear in the software itself,
*  if and wherever such third-party acknowledgments normally appear.
*
*  4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA"
*  must not be used to endorse or promote products derived from this
*  software without prior written permission. For written
*  permission, please contact Project JXTA at http://www.jxta.org.
*
*  5. Products derived from this software may not be called "JXTA",
*  nor may "JXTA" appear in their name, without prior written
*  permission of Sun.
*
*  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
*  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
*  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
*  DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
*  ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
*  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
*  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
*  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
*  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
*  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
*  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
*  SUCH DAMAGE.
*  ====================================================================
*
*  This software consists of voluntary contributions made by many
*  individuals on behalf of Project JXTA.  For more
*  information on Project JXTA, please see
*  <http://www.jxta.org/>.
*
*  This license is based on the BSD license adopted by the Apache Foundation.
*
*  $Id: StatusCommand.java,v 1.24 2007/04/16 20:14:05 nano Exp $
*/

package net.jxta.myjxta.dialog.commands;

import net.jxta.discovery.DiscoveryService;
import net.jxta.endpoint.EndpointService;
import net.jxta.endpoint.MessageTransport;
import net.jxta.id.ID;
import net.jxta.impl.endpoint.relay.RelayClient;
import net.jxta.impl.endpoint.relay.RelayServer;
import net.jxta.impl.rendezvous.PeerConnection;
import net.jxta.impl.rendezvous.RendezVousServiceInterface;
import net.jxta.impl.rendezvous.RendezVousServiceProvider;
import net.jxta.impl.rendezvous.StdRendezVousService;
import net.jxta.impl.rendezvous.rpv.PeerView;
import net.jxta.impl.rendezvous.rpv.PeerViewElement;
import net.jxta.logging.Logging;
import net.jxta.myjxta.MyJXTA;
import net.jxta.myjxta.dialog.Dialog;
import net.jxta.myjxta.dialog.DialogMessage;
import net.jxta.myjxta.util.AuthenticationUtil;
import net.jxta.peer.PeerID;
import net.jxta.peergroup.PeerGroup;
import net.jxta.peergroup.PeerGroupID;
import net.jxta.protocol.AccessPointAdvertisement;
import net.jxta.protocol.PeerAdvertisement;
import net.jxta.protocol.PeerGroupAdvertisement;
import net.jxta.protocol.RdvAdvertisement;
import net.jxta.rendezvous.RendezVousService;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * The The base Dialog for Group Chats.
 *
 * @version $Id: StatusCommand.java,v 1.24 2007/04/16 20:14:05 nano Exp $
 *
 * @author james todd [gonzo at jxta dot org]
 * @author mike mcangus [mcangus at jxta dot org]
 *
 */
public class StatusCommand
extends AbstractCommand {
    
    public static final String ID = StatusCommand.class.getName();
    public static final String STATUS = "status";
    public static final String PROFILE = "profile";
    public static final String VERSION = "version";
    public static final String TIME = "time";
    public static final String JAVA_VERSION = "java.version";
    public static final String JAVA_VENDOR = "java.vendor";
    public static final String OS_NAME = "os.name";
    public static final String OS_ARCHITECTURE = "os.architecture";
    public static final String PROCESSORS = "processors";
    public static final String JXTA_HOME = "jxta.home";
    public static final String FREE_MEMORY = "memory.free";
    public static final String MAXIMUM_MEMORY = "memory.maximum";
    public static final String TOTAL_MEMORY = "memory.total";
    public static final String RENDEZVOUS = "rendezvous";
    public static final String ROLE = "role";
    public static final String NAME = "name";
    public static final String UID = "id";
    public static final String GROUP_ID = "groupId";
    public static final String TYPE = "type";
    public static final String PEER = "peer";
    public static final String IS_AUTHENTICATED = "isAuthenticated";
    public static final String GROUP = "group";
    public static final String IS_ALIVE = "isAlive";
    public static final String IS_IN_PEER_VIEW = "isInPeerView";
    public static final String IS_THROTTLING = "isThrottling";
    public static final String NUMBER_OF_CONNECTED_PEERS = "numberOfConnectedPeers";
    public static final String START_TIME = "startTime";
    public static final String CREATE_TIME = "createTime";
    public static final String UPDATE_TIME = "updateTime";
    public static final String DIRECTION = "direction";
    public static final String ADDRESS = "address";
    public static final String UP = "up";
    public static final String DOWN = "down";
    public static final String CLIENT = "client";
    public static final String CLIENT_IS_CONNECTED = "isConnected";
    public static final String LEASE_TIME = "leaseTime";
    public static final String RELAYS = "relays";
    public static final String SERVER = "server";
    
    private static final String TIME_STAMP = "EEE MMM dd hh:mm:ss z yyyy";
    private static final SimpleDateFormat DATE_FORMATTER =
        new SimpleDateFormat(TIME_STAMP);
    private static final String ELEMENT_OPEN_PREAMBLE = "<";
    private static final String ELEMENT_OPEN_POSTAMBLE = ">";
    private static final String ELEMENT_OPEN_EMPTY_POSTAMBLE = "/" +
        ELEMENT_OPEN_POSTAMBLE;
    private static final String ELEMENT_CLOSE_PREAMBLE = "</";
    private static final String ELEMENT_CLOSE_POSTAMBLE = ELEMENT_OPEN_POSTAMBLE;
    private static final char EQUALS = '=';
    private static final char SPACE = ' ';
    private static final char QUOTE = '\"';
    private static final char SLASH = '\\';
    private static final char COMMA = ',';
    private static final char COLON = ':';
    private static final String UNICODE_ESCAPE= "/u";
    private static final int MAX_ASCII = 0x00007f;
    private static final String UNKNOWN = "unknown";
    private static final String PEER_TYPE_ID = "PID";
    private static final String GROUP_TYPE_ID = "GID";

    private static final Logger LOG = Logger.getLogger(StatusCommand.class.getName());

    public String getId() {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("In getID()");
        }
        return ID;
    }
    
    public boolean process(Dialog dialog, DialogMessage msg) {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("Begin process(Dialog, DialogMessage)");
        }

        String cmd = msg != null ? msg.getCommand() : null;
        PeerGroup pg = dialog.getGroup().getPeerGroup();
        DialogMessage dm = new DialogMessage(pg.getPeerName(), null,
            pg.getPeerGroupID().toString(), pg.getPeerGroupName());

        if (cmd != null &&
            cmd.length() > 0) {
            setCommand(cmd);
            setReply(getResponse(pg));
            setStatus(true);
            
            dm.setCommand(getResponse());
        }
        
        dialog.dispatch(dm);
        boolean retStatus = getStatus();
        
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("status = " + retStatus);
            LOG.fine("End   process(Dialog, DialogMessage)");
        }
        return retStatus;
    }
    
    public String getResponse(PeerGroup pg) {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("Begin getResponse(PeerGroup)");
        }

        StringBuffer sb = new StringBuffer();
        Runtime rt = Runtime.getRuntime();
        
        sb.append(open(STATUS) + ELEMENT_OPEN_POSTAMBLE + NEW_LINE);
        
        sb.append(TAB + open(PROFILE));
        sb.append(SPACE + attribute(VERSION, toUnicodeEncoded(MyJXTA.VERSION)));
        sb.append(SPACE + attribute(TIME,
            toUnicodeEncoded(DATE_FORMATTER.format(new Date(System.currentTimeMillis())))));
        sb.append(SPACE + attribute(JAVA_VERSION,
            toUnicodeEncoded(System.getProperty("java.version"))));
        sb.append(SPACE + attribute(JAVA_VENDOR,
            toUnicodeEncoded(System.getProperty("java.vendor"))));
        sb.append(SPACE + attribute(OS_NAME,
            toUnicodeEncoded(System.getProperty("os.name"))));
        sb.append(SPACE + attribute(OS_ARCHITECTURE,
            toUnicodeEncoded(System.getProperty("os.arch"))));
        sb.append(SPACE + attribute(PROCESSORS, rt.availableProcessors()));
        sb.append(SPACE + attribute(JXTA_HOME, "***")); //Env.getHome()));
        sb.append(SPACE + attribute(FREE_MEMORY, formatBytes(rt.freeMemory())));
        sb.append(SPACE + attribute(MAXIMUM_MEMORY, formatBytes(rt.maxMemory())));
        sb.append(SPACE + attribute(TOTAL_MEMORY, formatBytes(rt.totalMemory())));
        sb.append(close(PROFILE, false) + NEW_LINE);
        
        sb.append(TAB + open(PEER));
        sb.append(SPACE + attribute(UID, toUnicodeEncoded(pg.getPeerID().toString())));
        sb.append(SPACE + attribute(NAME, toUnicodeEncoded(pg.getPeerName())));
        sb.append(SPACE + attribute(IS_AUTHENTICATED,
            String.valueOf(AuthenticationUtil.isAuthenticated(pg))));
        sb.append(close(PEER, false) + NEW_LINE);
        
        RendezVousService rdv = pg != null ? pg.getRendezVousService() : null;
        
        if (rdv != null) {
            sb.append(TAB + open(RENDEZVOUS) + SPACE +
                attribute(ROLE, toUnicodeEncoded(rdv.getRendezVousStatus().toString().toLowerCase())) +
                ELEMENT_CLOSE_POSTAMBLE + NEW_LINE);

            StdRendezVousService srs = null;
        
            if (rdv instanceof RendezVousServiceInterface) {
                RendezVousServiceInterface rsi = (RendezVousServiceInterface)rdv;
        
                if (rsi != null) {
                    RendezVousServiceProvider rsp = rsi.getRendezvousProvider();
            
                    if (rsp instanceof StdRendezVousService) {
                        srs = (StdRendezVousService)rsp;
                    }
                }

                PeerView rpv = rsi.getPeerView();
                if (rpv!=null){
                    PeerViewElement[] pv = (PeerViewElement[])rpv.getView().toArray(new PeerViewElement[0]);

                    for (int pvi = pv.length - 1; pvi >= 0; pvi--) {
                        PeerViewElement pve = pv[pvi];
                        PeerData pd = new PeerData(pve.toString());

                        sb.append(TAB + TAB + open(PEER));

                        RdvAdvertisement ra = pve.getRdvAdvertisement();
                        String pid = ra != null ? ra.getPeerID().toString() : null;

                        if (pid != null) {
                            sb.append(SPACE + attribute(UID, toUnicodeEncoded(pid)));
                        }

                        sb.append(SPACE + attribute(NAME, toUnicodeEncoded(pd.getName())));

                        String gid = ra != null ? ra.getGroupID().toString() : null;

                        if (gid != null) {
                            sb.append(SPACE + attribute(GROUP_ID, toUnicodeEncoded(gid)));
                        }

                        sb.append(SPACE + attribute(IS_ALIVE,
                            toUnicodeEncoded(String.valueOf(pd.isAlive()))));
                        sb.append(SPACE + attribute(IS_IN_PEER_VIEW,
                            toUnicodeEncoded(String.valueOf(pd.isInPeerView()))));
                        sb.append(SPACE + attribute(IS_THROTTLING,
                            toUnicodeEncoded(String.valueOf(pd.isThrottling()))));
                        sb.append(SPACE + attribute(NUMBER_OF_CONNECTED_PEERS,
                            toUnicodeEncoded(String.valueOf(pd.getNumberOfConnectedPeers()))));
                        sb.append(SPACE + attribute(START_TIME,
                            toUnicodeEncoded(String.valueOf(pd.getStartTime()))));
                        sb.append(SPACE + attribute(CREATE_TIME,
                            toUnicodeEncoded(String.valueOf(pd.getCreateTime()))));
                        sb.append(SPACE + attribute(UPDATE_TIME,
                            toUnicodeEncoded(String.valueOf(pd.getUpdateTime()))));

                        String d = getDirection(pve, rpv);

                        if (d != null &&
                            d.trim().length() > 0) {
                            sb.append(SPACE + attribute(DIRECTION, toUnicodeEncoded(d)));
                        }

                        sb.append(close(PEER, false) + NEW_LINE);
                    }
                }
            }
            
            DiscoveryService dsc = pg.getDiscoveryService();
        
            if (rdv.isRendezVous()) {
                for (Enumeration ce = rdv.getConnectedPeers();
                    ce.hasMoreElements(); ) {
                    ID id = (ID)ce.nextElement();

                    if (srs != null) {
                        sb.append(toUnicodeEncoded(getClientStatus(srs.getPeerConnection(id))));
                    } else {
                        sb.append(toUnicodeEncoded(getClientStatus(dsc, id)));
                    }
                }
            } else {
                for (Enumeration ce = rdv.getConnectedRendezVous();
                    ce.hasMoreElements(); ) {
                    ID id = (ID)ce.nextElement();

                    if (srs != null) {
                        sb.append(toUnicodeEncoded(getRendezVousStatus(srs.getPeerConnection(id))));
                    } else {
                        sb.append(toUnicodeEncoded(getRendezVousStatus(dsc, id)));
                    }
                }
                            
                for (Enumeration re = rdv.getDisconnectedRendezVous();
                    re.hasMoreElements(); ) {
                    ID id = (ID)re.nextElement();
                        
                    sb.append(toUnicodeEncoded(getClientStatus(dsc, id)));
                }
            }

            sb.append(TAB + close(RENDEZVOUS) + NEW_LINE);
        }

        EndpointService es = pg.getEndpointService();
        
        sb.append(TAB + open(RELAYS) + ELEMENT_CLOSE_POSTAMBLE + NEW_LINE);
                    
        for (Iterator mti = es.getAllMessageTransports(); mti.hasNext(); ) {
            MessageTransport mt = (MessageTransport)mti.next();
            
            if (mt instanceof RelayClient) {
                Vector activeRelays = ((RelayClient) mt).getActiveRelays(null);
                if (activeRelays!=null){
                    for (Iterator ri = activeRelays.iterator(); ri.hasNext(); ) {
                        AccessPointAdvertisement ap = (AccessPointAdvertisement)ri.next();
                        String pid = ap.getPeerID().toString();
                        String pn = getPeerName(mt, ap);

                        sb.append(TAB + TAB + open(CLIENT));
                        sb.append(SPACE + attribute(UID, toUnicodeEncoded(pid)));

                        if (! pn.equals(pid)) {
                            sb.append(SPACE + attribute(NAME,
                                toUnicodeEncoded(getPeerName(mt, ap))));
                        }

                        sb.append(close(CLIENT, false) + NEW_LINE);
                    }
                }
            } else if (mt instanceof RelayServer) {
                for (Iterator ri =
                    ((RelayServer)mt).getRelayedClients().iterator();
                    ri.hasNext(); ) {
                    String s = (String)ri.next();
                    String pid = net.jxta.id.ID.URIEncodingName + COLON +
                        net.jxta.id.ID.URNNamespace + COLON +
                        s.substring(0, s.indexOf(COMMA));
                    String pn = getPeerName(mt, pid);
                            
                    sb.append(TAB + TAB + open(SERVER));
                    sb.append(SPACE + attribute(UID, toUnicodeEncoded(pid)));
                    
                    if (! pn.equals(pid)) {
                        sb.append(SPACE + attribute(NAME, toUnicodeEncoded(pn)));
                    }
                    
                    sb.append(close(SERVER, false) + NEW_LINE);
                }
            }
        }
        
        sb.append(TAB + close(RELAYS) + NEW_LINE);
        sb.append(close(STATUS));

        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("response = \'" + sb + "\'");
            LOG.fine("End   getResponse(PeerGroup)");
        }

        return sb.toString();
    }

    private String formatBytes(long l) {
        return ""+l/(float)1000+" kByte";
    }

    private String getDirection(PeerViewElement pve, PeerView pv) {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("In getDirection(PeerViewElement, PeerView)");
        }

        String d = "";

        if (pve == pv.getUpPeer()) {
            d = UP;
        } else if (pve == pv.getDownPeer()) {
            d = DOWN;
        }

        return d;
    }
        
    private String getClientStatus(PeerConnection pc) {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("In getClientStatus(PeerConnection)");
        }

        return getStatus(pc, CLIENT);
    }

    private String getClientStatus(DiscoveryService dsc, ID id) {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("In getClientStatus(DiscoveryService, ID)");
        }

        return getStatus(dsc, id, CLIENT);
    }
    
    private String getRendezVousStatus(PeerConnection pc) {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("In getRendezVousStatus(PeerConnection)");
        }

        return getStatus(pc, RENDEZVOUS);
    }
    
    private String getRendezVousStatus(DiscoveryService dsc, ID id) {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("In getRendezVousStatus(DiscoveryService, ID)");
        }

        return getStatus(dsc, id, RENDEZVOUS);
    }

    private String getStatus(PeerConnection pc, String e) {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("Begin getStatus(PeerConnection, String)");
        }

        StringBuffer sb = new StringBuffer();
        
        if (pc != null) {
            sb.append(TAB + TAB + open(e));
            sb.append(SPACE + attribute(UID, pc.getPeerID().toString()));
            sb.append(SPACE + attribute(NAME, pc.getPeerName()));
            sb.append(SPACE + attribute(TYPE, PEER));
            sb.append(SPACE + attribute(CLIENT_IS_CONNECTED,
                String.valueOf(pc.isConnected())));
            sb.append(SPACE + attribute(LEASE_TIME,
                String.valueOf(pc.getLeaseEnd())));
            sb.append(close(e, false) + NEW_LINE);
        }

        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("status = \'" + sb + "\'");
            LOG.fine("End   getStatus(PeerConnection, String)");
        }

        return sb.toString();
    }
    
    private String getStatus(DiscoveryService dsc, ID id, String e) {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("Begin getStatus(PeerConnection, ID, String)");
        }

        StringBuffer sb = new StringBuffer();
        
        if (dsc != null &&
            id != null) {
            String n = getIdName(dsc, id);
            String t = getIdType(id);
            
            sb.append(TAB + open(e));
            sb.append(SPACE + attribute(UID, id.toString()));
            
            if (n != null) {
                sb.append(SPACE + attribute(NAME, n));
            }
            
            if (t != null) {
                sb.append(SPACE + attribute(TYPE, t));
            }
            
            sb.append(close(e, false) + NEW_LINE);
        }

        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("status = \'" + sb + "\'");
            LOG.fine("End   getStatus(PeerConnection, ID, String)");
        }

        return sb.toString();
    }
    
    private String getIdName(DiscoveryService dsc, ID id) {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("Begin getIdName(DiscoveryService, ID)");
        }

        String n = null;
        String t = getIdType(id);
        
        if (t != null) {
            Enumeration e = null;

            if (t.equals(PEER)) {
                try {
                    e = dsc.getLocalAdvertisements(DiscoveryService.PEER,
                                                   PEER_TYPE_ID, id.toString());
                }
                catch (IOException ioe) {
                    if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
                        LOG.log(Level.SEVERE, "Caught unexpected exception ", ioe);
                    }
                }
                if (e != null && e.hasMoreElements()) {
                    n = ((PeerAdvertisement) e.nextElement()).getName();
                }
            } else if (t.equals(GROUP)) {
                try {
                    e = dsc.getLocalAdvertisements(DiscoveryService.GROUP,
                                                   GROUP_TYPE_ID, id.toString());
                }
                catch (IOException ioe) {
                    if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
                        LOG.log(Level.SEVERE, "Caught unexpected exception ", ioe);
                    }
                }
                if (e != null && e.hasMoreElements()) {
                    n = ((PeerGroupAdvertisement) e.nextElement()).getName();
                }
            }
        }

        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("name = \'" + n + "\'");
            LOG.fine("End   getIdName(DiscoveryService, ID)");
        }

        return n != null ? n : UNKNOWN;
    }
    
    private String getIdType(ID id) {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("Begin getIdType(DiscoveryService, ID)");
        }

        String t = null;
        
        if (id != null) {
            if (id instanceof PeerID) {
                t = PEER;
            } else if (id instanceof PeerGroupID) {
                t = GROUP;
            }
        }
        
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("type = \'" + t + "\'");
            LOG.fine("End   getIdType(DiscoveryService, ID)");
        }

        return t != null ? t : UNKNOWN;
    }
    
    private String getPeerName(MessageTransport mt, AccessPointAdvertisement adv ) {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("Begin getPeerName(MessageTransport, AccessPointAdvertisement)");
        }

        String pn = adv.getPeerID().toString();
        EndpointService es = mt.getEndpointService();
        DiscoveryService ds = es != null ?
            es.getGroup().getDiscoveryService() : null;
        
        try {
            for (Enumeration lae = ds.getLocalAdvertisements(DiscoveryService.PEER,
                "PID", pn);
                lae.hasMoreElements(); ) {
                pn = ((PeerAdvertisement)lae.nextElement()).getName();
                
                break;
            }
        } catch (IOException ioe) {
            if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
                LOG.log(Level.SEVERE, "Caught unexpected exception ", ioe);
            }
        }

        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("peerName = \'" + pn + "\'");
            LOG.fine("End   getPeerName(MessageTransport, AccessPointAdvertisement)");
        }

        return pn != null ? pn : UNKNOWN;
    }
    
    private String getPeerName(MessageTransport mt, String pid) {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("Begin getPeerName(MessageTransport, String)");
        }

        String pn = null;
        EndpointService es = mt.getEndpointService();
        DiscoveryService ds = es != null ?
            es.getGroup().getDiscoveryService() : null;
        
        if (ds != null) {
            try {
                for (Enumeration lae = ds.getLocalAdvertisements(DiscoveryService.PEER,
                    "PID", pid);
                    lae.hasMoreElements(); ) {
                    pn = ((PeerAdvertisement)lae.nextElement()).getName();
                    
                    break;
                }
            } catch (IOException ioe) {
                if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
                    LOG.log(Level.SEVERE, "Caught unexpected exception ", ioe);
                }
            }
        }

        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("peerName = \'" + pn + "\'");
            LOG.fine("End   getPeerName(MessageTransport, String)");
        }

        return pn != null ? pn : UNKNOWN;
    }
    
    private String open(String s) {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("In open(String)");
        }

        return ELEMENT_OPEN_PREAMBLE + s;
    }
    
    private String close(String s) {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("In open(String)");
        }

        return close(s, true);
    }
    
    private String close(String s, boolean hasContent) {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("In open(String, boolean)");
        }

        return hasContent ?
            ELEMENT_CLOSE_PREAMBLE + s + ELEMENT_CLOSE_POSTAMBLE :
            ELEMENT_OPEN_EMPTY_POSTAMBLE;
    }
    
    private String attribute(String n, String v) {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("In attribute(String, String)");
        }

        return n + EQUALS + quote(v);
    }
    
    private String attribute(String n, int v) {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("In attribute(String, int)");
        }

        return attribute(n, String.valueOf(v));
    }

    private String quote(String s) {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("In quote(String)");
        }

        StringBuffer sb = new StringBuffer();

        for (int i = 0; i < (s != null ? s.length() : 0); i++) {
            char c = s.charAt(i);

            if (c == QUOTE) {
                sb.append(SLASH);
            }

            sb.append(c);
        }

        return QUOTE + sb.toString() + QUOTE;
    }
       
    private String toUnicodeEncoded(String s) {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("In toUnicodeEncoded(String)");
        }

        StringBuffer b = null;
        
        if (s != null) {
            b = new StringBuffer();
            
            for (int i = 0; i < s.length(); i++) {
                char c = s.charAt(i);
                
                if (c <= MAX_ASCII) {
                    b.append(c);
                } else {
                    b.append(UNICODE_ESCAPE);
                    b.append(Integer.toHexString(c));
                }
            }
        }
        
        return b != null ? b.toString() : null;
    }
}

class PeerData {
    
    private static final char QUOTE = '\"';
    private static final char SLASH = '/';
    private static final char SPACE = ' ';
    private static final char OPEN_BRACKET = '[';
    private static final char CLOSE_BRACKET = ']';
    private static final char OPEN_PARENTHESIS = '(';
    private static final char CLOSE_PARENTHESIS = ')';
    private static final char IS_ALIVE = 'A';
    private static final char IS_IN_PEER_VIEW = 'P';
    private static final char IS_THROTTLING = 'T';

    private static final Logger LOG = Logger.getLogger(PeerData.class.getName());
    
    private String name = null;
    private boolean isAlive = false;
    private boolean isInPeerView = false;
    private boolean isThrottling = false;
    private int numberOfConnectedPeers = 0;
    private long startTime = 0;
    private long createTime = 0;
    private long updateTime = 0;

    public PeerData(String s) {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("Begin PeerData(String) Constructor");
        }

        int index = 0;
        StringBuffer sb = new StringBuffer();
        
        for (int i = 0; s != null && i < s.length(); i++) {
            char c = s.charAt(i);

            switch (index) {
              case 0:
                if (c == QUOTE) {
                    index++;
                }
                break;
              case 1:
                if (c != QUOTE || (c == QUOTE && s.charAt(i - 1) == SLASH)) {
                    sb.append(c);
                } else {
                    index++;
                    this.name = sb.toString();
                    sb.delete(0, sb.length());
                }
                break;
              case 2:
                if (c != SPACE) {
                    index++;
                    this.isAlive = c == IS_ALIVE;
                }
                break;
              case 3:
                if (c != SPACE) {
                    index++;
                    this.isInPeerView = c == IS_IN_PEER_VIEW;
                }
                break;
              case 4:
                if (c != SPACE) {
                    index++;
                    this.isThrottling = c == IS_THROTTLING;
                }
                break;
              case 5:
                if (c == OPEN_PARENTHESIS) {
                    index++;
                } else if (c == OPEN_BRACKET) {
                    index += 2;
                }
                break;
              case 6:
                if (c == CLOSE_PARENTHESIS) {
                    index--;
                    try {
                        this.numberOfConnectedPeers = Integer.valueOf(sb.toString()).intValue();
                    }
                    catch (NumberFormatException nfe) {
                        if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
                            LOG.log(Level.SEVERE, "Caught unexpected exception ",
                                    nfe);
                        }
                    }
                    sb.delete(0, sb.length());
                } else {
                    sb.append(c);
                }
                break;
              case 7:
                if (c == SLASH) {
                    index++;
                    try {
                        this.startTime = Long.valueOf(sb.toString()).longValue();
                    }
                    catch (NumberFormatException nfe) {
                        if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
                            LOG.log(Level.SEVERE, "Caught unexpected exception ",
                                    nfe);
                        }
                    }
                    sb.delete(0, sb.length());
                } else {
                    sb.append(c);
                }
                break;
              case 8:
                if (c == CLOSE_BRACKET) {
                    index += 2;
                    try {
                        this.updateTime = Long.valueOf(sb.toString()).longValue();
                    }
                    catch (NumberFormatException nfe) {
                        if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
                            LOG.log(Level.SEVERE, "Caught unexpected exception ",
                                    nfe);
                        }
                    }
                    this.createTime = this.startTime;
                    this.startTime = 0;
                    sb.delete(0, sb.length());
                } else if (c == SLASH) {
                    index++;
                    try {
                        this.createTime = Long.valueOf(sb.toString()).longValue();
                    }
                    catch (NumberFormatException nfe) {
                        if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
                            LOG.log(Level.SEVERE, "Caught unexpected exception ",
                                    nfe);
                        }
                    }
                    sb.delete(0, sb.length());
                } else {
                    sb.append(c);
                }
                break;
              case 9:
                if (c == CLOSE_BRACKET) {
                    index++;
                    try {
                        this.updateTime = Long.valueOf(sb.toString()).longValue();
                    }
                    catch (NumberFormatException nfe) {
                        if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
                            LOG.log(Level.SEVERE, "Caught unexpected exception ",
                                    nfe);
                        }
                    }
                    sb.delete(0, sb.length());
                } else {
                    sb.append(c);
                }
                break;
            }
        }

        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("End   PeerData(String) Constructor");
        }
    }

    public String getName() {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("In getName()");
        }

        return this.name;
    }
    
    public boolean isAlive() {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("In isAlive()");
        }

        return this.isAlive;
    }
    
    public boolean isInPeerView() {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("In inPeerView()");
        }

        return this.isInPeerView;
    }
    
    public boolean isThrottling() {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("In isThrottling()");
        }

        return this.isThrottling;
    }

    public int getNumberOfConnectedPeers() {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("In getNumberOfConnectedPeers()");
        }

        return this.numberOfConnectedPeers;
    }
    
    public long getStartTime() {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("In getStartTime()");
        }

        return this.startTime;
    }
    
    public long getCreateTime() {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("In getCreateTime()");
        }

        return this.createTime;
    }
    
    public long getUpdateTime() {
        if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
            LOG.fine("In getUpdateTime()");
        }

        return this.updateTime;
    }
}
